/*
* Copyright (c) 2006, obsoleet industries
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of obsoleet industries nor the names of its
*       contributors may be used to endorse or promote products derived from
*       this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


using System;
using System.Threading;
using System.Collections.Generic;
using System.Text;
using libsecondlife;
using libsecondlife.Packets;
using Meebey.SmartIrc4net;


namespace slirc
{
    partial class SlircNG
    {
        //GLOBAL VARIABLES ####################################################
        SecondLife Client = new SecondLife();
        
		struct avatarCache {
			public LLUUID key;
			public string name;
			public LLVector3 pos;
			public uint timestamp;
			public uint localID;		//hurr
		}
        List<avatarCache> avatars;
        //Dictionary<uint, PrimObject> prims;
        string platform;
        static bool logout = false;
        IrcClient myClient = new IrcClient();
        Thread myListenThread;
        Dictionary<LLUUID, IrcClient> ircconns = new Dictionary<LLUUID, IrcClient>();
        Dictionary<LLUUID, Thread> listenthreads = new Dictionary<LLUUID, Thread>();

        string configFile = "slirc.txt";
        public string firstName;
        public string lastName;
        public string password;
        bool relayChat = false;
        bool relaySL = true;
        bool relayIRC = true;
        bool uniqueIRCNick = false;
        string ircServer;
        string ircChannel;
        string myNick;

        //debugging stuff
        bool Debug = true;
        bool debugConsole = true;
        bool debugIrc = false;
        bool debugChat = false;
        int debugLevel = 5;

        string aclFile = "acl.txt";
        List<string> allowedIrc = new List<string>();
        List<LLUUID> allowedSL = new List<LLUUID>();
        string masterIrc = null;
        LLUUID masterSL = new LLUUID();

        string campFile = "camp.txt";
        string currentCamp = null;
        struct campPosition {
            public string name;
            public string sim;     //where
            public LLVector3 pos;  //pos
            public LLUUID key;     //key to sit on (if null sitg)
        }
        List<campPosition> camps = new List<campPosition>();
        ManualResetEvent teleportEvent = new ManualResetEvent(false);

        Timer campCheckTimer;
		Timer avatarCacheTimer;

        static void Main(string[] args)
        {
            SlircNG ghetto;
            if (args.Length == 0)
            {
                ghetto = new SlircNG(null);
            }
            else if (args.Length == 1)
            {
                ghetto = new SlircNG(args[0]);
            }
            else
            {
                Console.WriteLine("Usage: SlircNG [configfile]");
                return;
            }
            if (logout)
            {
                return;
            }
            while (! logout )
            {
                System.Threading.Thread.Sleep(1);
            }
            ghetto.Logout();
            while (ghetto.Client.Network.Connected)
            {
                System.Threading.Thread.Sleep(1);
            }
            ghetto.debug("END", 3);
        }
        public SlircNG(string filename)
        {
            platform = System.Convert.ToString(Environment.OSVersion.Platform);
            debug("Running on platform " + platform,3);

            if (filename != null)
            {
                configFile = filename;
            }
            debug("Using config file " + configFile, 2);
            bool check = readConfig(configFile);
            if (!check)
            {
                debug("Error reading config file", 1);
                logout = true;
                return;
            }
            check = readAcl(aclFile);
            if (!check)
            {
                debug("Error reading ACL File", 2);
            }
            check = readCamps(campFile);
            if (!check)
            {
                debug("Error reading camps file", 2);
            }

            avatars = new List<avatarCache>();
            //prims = new Dictionary<uint, PrimObject>();

            Client.Debug = Debug;

            Client.Self.Status.Camera.Far = 96.0f;
            Client.Self.Status.Camera.CameraAtAxis = new LLVector3(0, 0, 0);
            Client.Self.Status.Camera.CameraCenter = new LLVector3(0, 0, 0);
            Client.Self.Status.Camera.CameraLeftAxis = new LLVector3(0, 0, 0);
            Client.Self.Status.Camera.CameraUpAxis = new LLVector3(0, 0, 0);
            Client.Self.Status.Camera.HeadRotation = new LLQuaternion(0, 0, 0, 1);
            Client.Self.Status.Camera.BodyRotation = new LLQuaternion(0, 0, 0, 1);


            //setup master / ACL's
            //masterSL = "b9ccc481-d517-41fc-b7ee-ae12676e70dc";
            //masterIrc = @"~?rewt@.*eghetto\.ca";

            debug("SL Master Key - " + masterSL.ToString(), 3);
            debug("IRC Master Netmask - " + masterIrc, 3);
            
            //set "home" location
            if (camps.Count < 1)
            {
                debug("No camps defined, assigning default home.", 2);
                campPosition home = new campPosition();
                home.name = "home";
                home.sim = "Pi";
                home.pos = new LLVector3(30, 30, 14);
                home.key = new LLUUID("c7b054dc-a963-6817-274a-3390a28577fb");
                camps.Add(home);
            }
            //Add callbacks for events
            InitializeCallbacks();
            campCheckTimer = new Timer(new TimerCallback(OnCampTimer));
			avatarCacheTimer = new Timer(new TimerCallback(AvatarCacheTimerHandler));
			avatarCacheTimer.Change(300000, 300000);
            //Attempt to login, and exit if failed
            while (!Login()) Thread.Sleep(5000);

            //attempt to login to IRC
            if (myNick == null)
            {
                myNick = firstName + lastName;
            }
            check = ircConnect(myNick);
            if (!check)
            {
                debug("Unable to connect to control channel " + myNick + " on IRC server " + ircServer + ".", 1);
                logout = true;
                return;
            }
            //Accept commands
        }
        static void avatarListen(object dumbThings)
        {
            IrcClient ircclient = (IrcClient)dumbThings;
            ircclient.Listen();
        }
        static void commandListen(object dumbThings)
        {
            IrcClient ircclient = (IrcClient)dumbThings;
            ircclient.Listen();
        }
        public bool ircConnect(string ircname)
        {
            return ircConnect(new LLUUID(), ircname, true);
        }
        public bool ircConnect(LLUUID agent, string ircname, bool commandChan)
        {
            IrcClient tempClient = new IrcClient();
            tempClient.Connect(ircServer, 6667);
            tempClient.Login(ircname, "SL Client");
            myNick = tempClient.Nickname;
            debug("Connected to IRC Server " + ircServer + " as " + myNick + " on channel " + ircChannel, 3, true, false, true);
            tempClient.RfcJoin(ircChannel);
            if (commandChan)
            {
                lock ( myClient ) {
                    myClient = tempClient;
                }
				myClient. SendDelay = 500;
                myListenThread = new Thread(new ParameterizedThreadStart(commandListen));
                myListenThread.Start(myClient);
                
            }
            else
            {
                lock (ircconns)
                {
                    ircconns[agent] = tempClient;
					ircconns[agent].SendDelay = 500;
                } 
                lock (listenthreads)
                {
                    if (listenthreads.ContainsKey(agent)) listenthreads[agent].Abort();
                    listenthreads[agent] = new Thread(new ParameterizedThreadStart(avatarListen));
                    listenthreads[agent].Start(ircconns[agent]);
                }
            }

            initIRCCallbacks();
            return true;
        }
        bool Login()
        {
            debug("Logging into SL as " + firstName + " " + lastName, 3);

            //Attempt to log in
            if (!Client.Network.Login(firstName, lastName, password, "SlircNG", "slirc@obsoleet.com"))
            {
                debug("Login failed.", 1);
                return false;
            }

            //Succeeded - Wait for simulator name or disconnection
            Simulator sim = Client.Network.CurrentSim;
            while (Client.Network.Connected && (!sim.Connected || sim.Region.Name == "" || Client.Grid.SunDirection.X == 0))
            {
                Thread.Sleep(100);
            }

            //Halt if disconnected
            if (!Client.Network.Connected) return false;

            //We are in!
            debug(RPGWeather(), 3);
            debug("Location: " + Client.Self.Position, 4, true, true, false);

            //Fix the "bot squat" animation
            Client.Self.Status.SendUpdate();

            return true;
        }
        void Logout()
        {
            Client.Network.Logout();
            //close down extra threads and irc
            foreach (KeyValuePair<LLUUID, Thread> dictEnum in listenthreads)
            {
                dictEnum.Value.Abort();
            }
            foreach (KeyValuePair<LLUUID, IrcClient> dictEnum in ircconns)
            {
                dictEnum.Value.RfcQuit("relay closed");
            }
            myListenThread.Abort();
            myClient.RfcQuit("slirc exit");
            while (Client.Network.Connected)
            {
                Client.Tick();
            }
            debug("Exit", 3);
        }
    }
}
